package ssq.gamest.game.ai;

import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import java.util.Vector;

import ssq.gamest.game.PokerCardGroup;
import ssq.gamest.game.PokerPattern;
import ssq.utils.OrderedList;
import ssq.utils.Pair;

public abstract class PokerAI extends AI
{
    PokerPattern                                                                              noMore  = new PokerPattern()
                                                                                                      {
                                                                                                          @Override
                                                                                                          public String toString()
                                                                                                          {
                                                                                                              return "NoMore";
                                                                                                          }
                                                                                                          
                                                                                                          @Override
                                                                                                          public List<Pair<PokerPattern, PokerCardGroup>> match(PokerCardGroup pokerCardGroup, boolean partial, boolean first)
                                                                                                          {
                                                                                                              return null;
                                                                                                          }
                                                                                                      };
    public PokerAiSettings                                                                    settings;
    private HashMap<PokerCardGroup, Pair<double[], List<Pair<PokerPattern, PokerCardGroup>>>> history = new HashMap<PokerCardGroup, Pair<double[], List<Pair<PokerPattern, PokerCardGroup>>>>();
    
    /**
     * 这是match函数的重要应用, 将几张牌分成几套牌的组合. 如果有多个解, 按照ai配置的权重计算方法计算最佳分解 <br/>
     * 特别注意: 搜索全状态空间非常慢, 采用历史表加快搜索. 这要求权重表达式的选取使得牌型分割有最优子结构性质
     *
     * @return (这手牌的权重, 被分割的组)
     */
    public Pair<Double, List<Pair<PokerPattern, PokerCardGroup>>> divideByPattern(PokerCardGroup pokerCardGroup)
    {
        Stack<Pair<PokerPattern, PokerCardGroup>> stack = new Stack<Pair<PokerPattern, PokerCardGroup>>();
        stack.add(new Pair<PokerPattern, PokerCardGroup>(null, pokerCardGroup));
        Pair<double[], List<Pair<PokerPattern, PokerCardGroup>>> result = divideByPattern(new Search(stack), 0, 0);
        return new Pair<Double, List<Pair<PokerPattern, PokerCardGroup>>>(result.getKey()[0], refine(result.getValue()));
    }
    
    static class Search
    {
        public Search(Stack<Pair<PokerPattern, PokerCardGroup>> stack)
        {
            this.stack = stack;
        }
        
        public Stack<Pair<PokerPattern, PokerCardGroup>> stack = new Stack<Pair<PokerPattern, PokerCardGroup>>();
        public double                                    best  = Double.MIN_VALUE;
    }
    
    /**
     *
     * @param search
     *            纵向搜索栈和本次搜索的最佳权重
     * @param sum
     *            牌的权重和
     * @param hands
     *            道数
     * @return <[totalWeight, sum, residual], 每个深度下的: 上一层的牌型选择->剩余牌>
     */
    @SuppressWarnings("unchecked")
    private Pair<double[], List<Pair<PokerPattern, PokerCardGroup>>> divideByPattern(Search search, double sum, double hands)
    {
        Stack<Pair<PokerPattern, PokerCardGroup>> stack = search.stack;
        Stack<Pair<PokerPattern, PokerCardGroup>> choices = new Stack<Pair<PokerPattern, PokerCardGroup>>();
        PokerCardGroup pokerCardGroup = stack.peek().getValue();
        
        Pair<double[], List<Pair<PokerPattern, PokerCardGroup>>> p = history.get(pokerCardGroup);
        if (p != null)
        {
            List<Pair<PokerPattern, PokerCardGroup>> v = p.getValue();
            double[] k = p.getKey();
            
            double residual = k[1];
            sum += k[0];
            hands += v.size();
            double totalWeight = getTotalWeight(sum, hands, residual);
            List<Pair<PokerPattern, PokerCardGroup>> wholeStack = (List<Pair<PokerPattern, PokerCardGroup>>) stack.clone();
            wholeStack.addAll(v);
            
            return new Pair<double[], List<Pair<PokerPattern, PokerCardGroup>>>(new double[] { totalWeight, sum, residual }, wholeStack);
        }
        
        if (pokerCardGroup.size() >= 1)
        {
            for (PokerPattern pattern : settings.game.settings.patternTopologySort)
            {
                try
                {
                    List<Pair<PokerPattern, PokerCardGroup>> result = pattern.match(pokerCardGroup, true, false);
                    
                    choices.addAll(result);
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        }
        
        checkStates(search, choices, sum, hands);
        
        if (choices.size() == 0) //没得选了就返回权重和剩余的牌
        {
            double residual = pokerCardGroup.size();
            double totalWeight = getTotalWeight(sum, hands, residual);
            search.best = Math.max(search.best, totalWeight);
            return new Pair<double[], List<Pair<PokerPattern, PokerCardGroup>>>(new double[] { totalWeight, sum, residual }, (List<Pair<PokerPattern, PokerCardGroup>>) stack.clone());
        }
        
        Pair<double[], List<Pair<PokerPattern, PokerCardGroup>>> best = new Pair<double[], List<Pair<PokerPattern, PokerCardGroup>>>(new double[] { Double.MIN_VALUE, 0, 0, 0 }, null);
        for (Pair<PokerPattern, PokerCardGroup> pair : choices)
        {
            stack.push(pair);
            
            Pair<double[], List<Pair<PokerPattern, PokerCardGroup>>> result = divideByPattern(search, sum + settings.getWeight(pair.getKey()), hands + 1);
            
            stack.pop();
            
            if (result.getKey()[0] > best.getKey()[0])
            {
                best = result;
            }
        }
        
        // 保存当前的pokerCardGroup的最优分割
        List<Pair<PokerPattern, PokerCardGroup>> stack1 = best.getValue();
        List<Pair<PokerPattern, PokerCardGroup>> subList = stack1.subList(stack1.indexOf(pokerCardGroup) + 1, stack1.size());
        updateHistory(pokerCardGroup, best.getKey()[1] - sum, best.getKey()[2], subList);
        return best;
    }
    
    /**
     * 按贪心算法选出所有可能的结果中比较靠谱的1个或0个
     *
     * @param search
     * @param rawResult
     * @return
     */
    private void checkStates(Search search, List<Pair<PokerPattern, PokerCardGroup>> rawResult, final double sum, final double hands)
    {
        final HashMap<Pair<PokerPattern, PokerCardGroup>, Double> weights = new HashMap<Pair<PokerPattern, PokerCardGroup>, Double>();
        for (Iterator<Pair<PokerPattern, PokerCardGroup>> iterator = rawResult.iterator(); iterator.hasNext();)
        {
            Pair<PokerPattern, PokerCardGroup> pair = iterator.next();
            weights.put(pair, evaluate(pair.getValue(), sum + settings.getWeight(pair.getKey()), hands + 1));
        }
        
        OrderedList<Pair<PokerPattern, PokerCardGroup>> orderedList = new OrderedList<Pair<PokerPattern, PokerCardGroup>>(new Comparator<Pair<PokerPattern, PokerCardGroup>>()
                {
            @Override
            public int compare(Pair<PokerPattern, PokerCardGroup> o1, Pair<PokerPattern, PokerCardGroup> o2)
            {
                return weights.get(o2) > weights.get(o1) ? 1 : -1;
            }
                });
        
        for (int i = 0; i < rawResult.size() - 1; i++)
        {
            Pair<PokerPattern, PokerCardGroup> pair = rawResult.get(i);
            
            if (weights.get(pair) > search.best / 2)
            {
                orderedList.add(pair);
                
                if (orderedList.size() > 3)
                {
                    orderedList.remove(3);
                }
            }
        }

        rawResult.clear();
        
        for (Pair<PokerPattern, PokerCardGroup> pair : orderedList)
        {
            rawResult.add(pair);
        }
    }
    
    /**
     * 贪心算法估值函数
     *
     * @param pokerCardGroup
     *            当前的状态
     * @param sum
     *            当前的sum
     * @param hands
     *            当前的hands
     * @return
     */
    private double evaluate(PokerCardGroup pokerCardGroup, double sum, double hands)
    {
        Pair<double[], List<Pair<PokerPattern, PokerCardGroup>>> p = history.get(pokerCardGroup);
        if (p != null)
        {
            List<Pair<PokerPattern, PokerCardGroup>> v = p.getValue();
            double[] k = p.getKey();
            
            double residual = k[1];
            sum += k[0];
            hands += v.size();
            double totalWeight = getTotalWeight(sum, hands, residual);
            
            return totalWeight;
        }
        
        if (pokerCardGroup.size() >= 1)
        {
            for (PokerPattern pattern : settings.game.settings.patternTopologySort)
            {
                try
                {
                    List<Pair<PokerPattern, PokerCardGroup>> result = pattern.match(pokerCardGroup, true, false);
                    
                    if (result.size() > 0)
                    {
                        return evaluate(result.get(0).getValue(), settings.getWeight(result.get(0).getKey()), hands + 1);
                    }
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
            double residual = pokerCardGroup.size();
            double totalWeight = getTotalWeight(sum, hands, residual);
            return totalWeight;
        }
        
        return getTotalWeight(sum, hands, pokerCardGroup.size());
    }
    
    /**
     * 将当前状态的最优解保存到历史表<br/>
     * 特别注意: state是直接放入的, 这都基于state再也不发生变化的假设
     *
     * @param pokerCardGroup
     * @param i
     * @param value
     */
    private void updateHistory(PokerCardGroup state, double bestSum, double bestResidual, List<Pair<PokerPattern, PokerCardGroup>> subList)
    {
        history.put(state, new Pair<double[], List<Pair<PokerPattern, PokerCardGroup>>>(new double[] { bestSum, bestResidual }, subList));
    }
    
    /**
     * 将结果整理好
     *
     * @param list
     * @return
     */
    private List<Pair<PokerPattern, PokerCardGroup>> refine(List<Pair<PokerPattern, PokerCardGroup>> list)
    {
        Vector<Pair<PokerPattern, PokerCardGroup>> result = new Vector<Pair<PokerPattern, PokerCardGroup>>(list);
        for (int i = 1; i < result.size(); i++)
        {
            result.get(i - 1).setKey(result.get(i).getKey());
        }
        
        if (result.get(result.size() - 1).getValue().size() == 0)
        {
            result.remove(result.size() - 1);
        }
        else
        {
            result.get(result.size() - 1).setKey(noMore);
        }
        
        for (int i = result.size() - 1; i >= 0; i--)
        {
            
            PokerCardGroup lastToBeDeleted = result.get(i).getValue();
            
            for (int j = 0; j < i; j++)
            {
                result.get(j).getValue().removeAll(lastToBeDeleted);
            }
        }
        
        return result;
    }
    
    public List<Pair<PokerPattern, PokerCardGroup>> containsBigger(PokerCardGroup toBeChecked, PokerPattern thePattern)
    {
        Class<? extends PokerPattern> theClass = thePattern.getClass();
        List<Pair<PokerPattern, PokerCardGroup>> result = new LinkedList<Pair<PokerPattern, PokerCardGroup>>();
        
        for (PokerPattern pattern : toBeChecked.game.settings.patternTopologySort)
        {
            Class<? extends PokerPattern> clazz = pattern.getClass();
            
            int compare = toBeChecked.game.settings.getOrder(clazz, theClass);
            
            if (compare > 0)
            {
                result.addAll(pattern.match(toBeChecked, true, false));
            }
            else if (compare == 0)
            {
                List<Pair<PokerPattern, PokerCardGroup>> tmp = pattern.match(toBeChecked, true, false);
                
                for (Iterator<Pair<PokerPattern, PokerCardGroup>> iterator = tmp.iterator(); iterator.hasNext();)
                {
                    Pair<PokerPattern, PokerCardGroup> pair = iterator.next();
                    
                    if (pair.getKey().compareTo(thePattern) <= 0)
                    {
                        iterator.remove();
                    }
                }
                
                result.addAll(tmp);
                
                return result;
            }
        }
        
        return result;
    }
    
    protected abstract double getTotalWeight(double sum, double hands, double residual);
    
    public PokerAI(PokerAiSettings s)
    {
        settings = s;
    }
}
